﻿// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk)
//
// This file is part of SharpMap.
// SharpMap is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// SharpMap is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public License
// along with SharpMap; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
// for petra.gis adopted by asida

using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using Petra.Gis.Geometry.Support;
//using Petra.Gis.ReferenceSystemServices.Transformations;



namespace Petra.Gis.ReferenceSystemServices
{
  
  /// <summary>
  /// Base interface for all coordinate systems.
  /// </summary>
  /// <remarks>
  /// <para>A coordinate system is a mathematical space, where the elements of the space
  /// are called positions. Each position is described by a list of numbers. The length 
  /// of the list corresponds to the dimension of the coordinate system. So in a 2D 
  /// coordinate system each position is described by a list containing 2 numbers.</para>
  /// <para>However, in a coordinate system, not all lists of numbers correspond to a 
  /// position - some lists may be outside the domain of the coordinate system. For 
  /// example, in a 2D Lat/Lon coordinate system, the list (91,91) does not correspond
  /// to a position.</para>
  /// <para>Some coordinate systems also have a mapping from the mathematical space into 
  /// locations in the real world. So in a Lat/Lon coordinate system, the mathematical 
  /// position (lat, long) corresponds to a location on the surface of the Earth. This 
  /// mapping from the mathematical space into real-world locations is called a Datum.</para>
  /// </remarks>
  [DataContract]
  public abstract partial class CoordinateSystem
  {



    /// <summary>
    /// xaml constr.
    /// </summary>
    protected CoordinateSystem() 
    {
      Axes = new AxisList();
    }





    /// <summary>
    /// Initializes a new instance of a coordinate system.
    /// </summary>
    /// <param name="storageUnit">coordinate storage units</param>
    /// <param name="axis">list of axes</param>
    /// <param name="metadata">coordinate system metadata</param>
    protected CoordinateSystem(MeasureUnit storageUnit, AxisList axis, Metadata metadata)
    {

      Unit = storageUnit;
      Axes = axis;
      Metadata = metadata;
    }





    protected CoordinateSystem(MeasureUnit storageUnit, AxisList axis, Metadata metadata, BoundingBox bounds)
      : this(storageUnit, axis, metadata)
    {

      DefaultEnvelope = bounds;
    }




    protected CoordinateSystem(string name, MeasureUnit storageUnit, AxisList axis)
      : this(storageUnit, axis, new Metadata(name,"",-1,"","",""))
    {

    }


    #region CoordinateSystem Members

    /// <summary>
    /// Dimension of the coordinate system.
    /// </summary>
    public int Dimension
    {
      get { return Axes.Count; }
    }




    //protected AxisList _Axes;
    /// <summary>
    /// List of coordinate system axes
    /// </summary>
    [DataMember(Name="AXES")]
    public AxisList Axes
    { get; set; }




    //private BoundingBox _DefaultEnvelope;

    /// <summary>
    /// Gets default envelope of coordinate system.
    /// </summary>
    /// <remarks>
    /// Coordinate systems which are bounded should return the minimum bounding box of their domain. 
    /// Unbounded coordinate systems should return a box which is as large as is likely to be used. 
    /// For example, a (lon,lat) geographic coordinate system in degrees should return a box from 
    /// (-180,-90) to (180,90), and a geocentric coordinate system could return a box from (-r,-r,-r)
    /// to (+r,+r,+r) where r is the approximate radius of the Earth.
    /// </remarks>
    [DataMember(Name="BBOX")]
    public BoundingBox DefaultEnvelope
    { get; set; }



    //protected Metadata _metadata;

    /// <summary>
    /// set or return coordinate system metadata
    /// </summary>
    [DataMember(Name="INFO")]
    public Metadata Metadata
    { get; set; }





    //protected SpatialUnit _storageUnits;

    /// <summary>
    /// sets or return coordinate system storage units
    /// </summary>
    [DataMember(Name="UNIT")]
    public virtual MeasureUnit Unit
    { get; set; }





    /// <summary>
    /// Checks whether the values of this instance is equal to the values of another instance.
    /// Only parameters used for coordinate system are used for comparison.
    /// Metadata are skipped.
    /// </summary>
    /// <param name="obj"></param>
    /// <returns>True if equal</returns>
    public abstract bool EqualParams(CoordinateSystem obj, CsCompareType compareType);


    #endregion


    /// <summary>
    /// create clone (separate copy) of current coordinate system
    /// </summary>
    /// <returns></returns>
    public CoordinateSystem Clone()
    {
      string tmp = System.Windows.Markup.XamlWriter.Save(this);
      return (CoordinateSystem)System.Windows.Markup.XamlReader.Parse(tmp);
    }




    #region staticMembers


    public static CoordinateSystem Parse(string strUTF8Json)
    {

      System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
      MemoryStream ms = new MemoryStream(encoding.GetBytes(strUTF8Json));

      //try parse ProjectedCoordinateSystem
      try
      {
        DataContractJsonSerializer json = new DataContractJsonSerializer(typeof(ProjectedCoordinateSystem));
        return (ProjectedCoordinateSystem)json.ReadObject(ms);
      }
      catch (Exception e) { }

      //try parse GeographicCoordinateSystem
      try
      {
        DataContractJsonSerializer json = new DataContractJsonSerializer(typeof(GeographicCoordinateSystem));
        return (GeographicCoordinateSystem)json.ReadObject(ms);
      }
      catch (Exception e) { }

      //try parse GeocentricCoordinateSystem
      try
      {
        DataContractJsonSerializer json = new DataContractJsonSerializer(typeof(GeocentricCoordinateSystem));
        return (GeocentricCoordinateSystem)json.ReadObject(ms);
      }
      catch (Exception e) { }

      //else return null
      return null;
    }


    #endregion staticMembers

  }

}
